package com.mvc.security.service;

import java.util.LinkedList;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.stereotype.Service;

import com.mvc.framework.model.Auditable;
import com.mvc.framework.service.BaseService;
import com.mvc.security.model.Menu;
import com.mvc.security.model.Role;
import com.mvc.security.model.RoleMenu;
import com.mvc.security.model.UserRole;

@Service
public class MenuManager extends BaseService<Menu, Integer> {

	public void save(Menu entity) {
		boolean updateChildren = false;
		Integer menuId = entity.getObjectId();
		if (null == menuId) {
			menuId = createSystemId(entity.getClass());
			entity.setObjectId(menuId);
		} else {
			updateChildren = true;
		}
		setPath(entity);
		super.save(entity);
		if (updateChildren) {
			String parentPath = entity.getPath();
			updatePathOfDirectChildren(menuId, parentPath);
		}

	}

	private void updatePathOfDirectChildren(Integer parentId, String parentPath) {
		List<Menu> needUpdateMenu = getDirectChildren(parentId);
		Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
		for (Menu menu : needUpdateMenu) {
			menu.setPath(parentPath + Menu.PATH_SEPARATOR + menu.getName());
			updatePathOfDirectChildren(menu.getObjectId(), menu.getPath());
			String hql = "UPDATE " + Menu.class.getName() + " SET PATH = ? WHERE ID = ?";
			Query query = session.createQuery(hql).setParameter(0, menu.getPath()).setParameter(1, menu.getId());
			query.executeUpdate();
		}
	}

	private void setPath(Menu entity) {
		if (null != entity) {
			if (null != entity.getParentId() && !Menu.ROOT.getObjectId().equals(entity.getParentId())) {
				Menu parentMenu = getParentMenu(entity.getParentId());
				entity.setPath(parentMenu.getPath() + Menu.PATH_SEPARATOR + entity.getName());
			} else {
				entity.setPath(Menu.PATH_SEPARATOR + entity.getName());
			}
		}
	}

	public List<Menu> getMenusByServerId(int serverId) {
		Menu menu = new Menu();
		menu.setServerId(serverId);
		menu.setType(Menu.TYPE_MENU);
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		menu.setStatus(Menu.MENU_STATUS_ONLINE);
		return getHibernateTemplate().findByExample(menu);
	}

	public List<Menu> getAll() {
		Menu menu = new Menu();
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		return getHibernateTemplate().findByExample(menu);
	}

	public List<Menu> getAllMenu() {
		Menu menu = new Menu();
		menu.setType(Menu.TYPE_MENU);
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		return getHibernateTemplate().findByExample(menu);
	}

	public List<Menu> getMenusByServerIdAndUserId(Integer serverId, Integer userId) {
		String sql = "SELECT  distinct A from  " + Menu.class.getName() + " A, " + RoleMenu.class.getName() + " C, "
		        + UserRole.class.getName() + " D, " + Role.class.getName() + " E WHERE A.recStatus = " + Auditable.REC_STATUS_VALID
		        + " AND A.status = " + Menu.MENU_STATUS_ONLINE + " AND A.serverId = " + serverId + " AND A.objectId = C.menuId "
		        + "AND C.roleId = D.roleId AND D.userId = " + userId + " AND C.recStatus =" + Auditable.REC_STATUS_VALID
		        + " AND D.recStatus =" + Auditable.REC_STATUS_VALID + " AND D.roleId = E.objectId AND E.recStatus = " + Auditable.REC_STATUS_VALID ;
		return searchBySql(sql);
	}

	public List<Menu> getMenusByUserId(Integer userId) {
		String sql = "SELECT distinct A from  " + Menu.class.getName() + " A, " + RoleMenu.class.getName() + " C, "
		        + UserRole.class.getName() + " D, " + Role.class.getName() + " E WHERE A.recStatus = " + Auditable.REC_STATUS_VALID
		        + " AND C.recStatus = " + Auditable.REC_STATUS_VALID + " AND D.recStatus = " + Auditable.REC_STATUS_VALID
		        + " AND A.objectId = C.menuId " + "AND C.roleId = D.roleId AND D.userId = " + userId + " AND D.roleId = E.objectId AND E.recStatus = " + Auditable.REC_STATUS_VALID ;
		return searchBySql(sql);
	}

	public List<Menu> getMenusByRoleId(Integer roleId) {
		String sql = "SELECT distinct A FROM  " + Menu.class.getName() + " A, " + RoleMenu.class.getName()
		        + " C," + Role.class.getName() + " E WHERE A.recStatus = " + Auditable.REC_STATUS_VALID + " AND C.recStatus = " + Auditable.REC_STATUS_VALID 
		        + " AND A.status = " + Menu.MENU_STATUS_ONLINE + " AND A.type = " + Menu.TYPE_MENU
		        + " AND A.objectId = C.menuId AND C.roleId = " + roleId  + " AND C.roleId = E.objectId AND E.recStatus = " + Auditable.REC_STATUS_VALID ;
		return searchBySql(sql);
	}

	public List<Menu> getAllFolder() {
		Menu menu = new Menu();
		menu.setType(Menu.TYPE_FOLDER);
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		List<Menu> result = getHibernateTemplate().findByExample(menu);
		result.add(Menu.ROOT);
		return result;
	}

	public Menu getMenuByMenuId(Integer menuId) {
		Menu menu = new Menu();
		menu.setObjectId(menuId);
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		List<Menu> list = getHibernateTemplate().findByExample(menu);
		if (list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	public List<Menu> getDirectChildren(Integer parentId) {
		Menu menu = new Menu();
		menu.setParentId(parentId);
		menu.setStatus(Menu.MENU_STATUS_ONLINE);
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		return getHibernateTemplate().findByExample(menu);
	}

	public List<Menu> getChildren(Integer parentId) {
		Menu menu = new Menu();
		menu.setParentId(parentId);
		menu.setStatus(Menu.MENU_STATUS_ONLINE);
		menu.setRecStatus(Auditable.REC_STATUS_VALID);
		List<Menu> result = new LinkedList<Menu>();
		List<Menu> current = getHibernateTemplate().findByExample(menu);
		List<Menu> children = new LinkedList<Menu>();
		for (Menu temp : result) {
			children.addAll(getChildren(temp.getObjectId()));
		}
		result.addAll(children);
		result.addAll(current);
		return result;
	}

	public Menu getParentMenu(Integer parentId) {
		Menu menu = getMenuByMenuId(parentId);
		setPath(menu);
		return menu;
	}
}
